/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <flgrCoreData.h>
#include <flgrCoreDataIO.h>
#include <flgrCoreNhbManage.h>
#include <flgrCoreCopy.h>
#include <flgrCoreDispatch.h>

#include <flgrArith.h>

#include "flgrMorphoLabel.h"

/*!
 * \defgroup group_fulguro_core_morpho_label Labelling operations
 * \ingroup group_fulguro_morpho
 * \brief Geodesic operations
 * @{
 */



#define lbl(dtype,dtypename)						\
  int *stackRow, *stackCol;						\
  int *stackRowPush, *stackColPush;					\
  int *stackRowPop, *stackColPop;					\
  dtype *rowLabel, *rowSrc;						\
  dtype *rowLabel_current;						\
 									\
  FLGR_Data2D *nhbOdd;							\
  FLGR_Data2D *nhbEven;							\
  FLGR_Data2D *nhb;							\
  FLGR_NhbBox2D *extr;							\
  dtype *pextr;								\
  int *list_x;								\
  int *list_y;								\
  dtype label=0;							\
  int labelOverflow=0;							\
  int size_y = imglabel->size_y;					\
  int size_x = imglabel->size_x;					\
  int i,j,u,v,m,n,k,l,t;						\
  int currentPixel;							\
									\
  FLGR_DECLARE_FUNCTION;						\
									\
  flgr2d_clear_all(imglabel);						\
									\
  nhb=flgr2d_create_neighborhood_from_connexity(1,dtypename,connexity);	\
  flgr2d_set_data_array_##dtype((dtype **) nhb->array,1,1,0);		\
									\
  nhbOdd = flgr2d_create_neighborhood_from(nhb);			\
  nhbEven = flgr2d_create_neighborhood_from(nhb);			\
  									\
  flgr2d_fill_nhbs_for_6_connexity(nhbEven,nhbOdd,nhb,FLGR_NHB_NO_SYM);	\
  									\
  flgr2d_destroy(nhb);							\
									\
  extr = flgr2d_create_neighbor_box(nhbOdd);				\
  pextr=(dtype*) extr->list_data_val[0];				\
  list_x = extr->list_coord_x[0];					\
  list_y = extr->list_coord_y[0];					\
									\
  stackRow = (int*) malloc(sizeof(int)*size_y*size_x);			\
  stackCol = (int*) malloc(sizeof(int)*size_y*size_x);			\
  stackRowPush = stackRowPop = stackRow;				\
  stackColPush = stackColPop = stackCol;				\
									\
  for(i=0 ; i<size_y ; i++) {						\
									\
    rowLabel = imglabel->array[i];					\
    rowSrc = imgsrc->array[i];						\
									\
    nhb = (((i%2)==1) ? nhbOdd : nhbEven);				\
									\
    for(j=0 ; j<size_x ; j++) {						\
									\
      if((rowLabel[j]==0) && (rowSrc[j]!=0)) {				\
									\
	currentPixel=rowSrc[j];						\
									\
	label = (label + 1);						\
	if(label==0) {							\
	  label=1;							\
	  labelOverflow=1;						\
	}								\
									\
	rowLabel[j]=label;						\
									\
	flgr2d_get_neighborhood_##dtype(extr,imgsrc,nhb, i, j);		\
									\
	for(t=0 ; t<extr->size[0] ; t++) {				\
	  u=list_y[t];							\
	  v=list_x[t];							\
	  m=i+u-1;							\
	  n=j+v-1;							\
	  rowLabel_current = imglabel->array[m];			\
	  if(pextr[t]==currentPixel) {					\
	    *stackRowPush=m;						\
	    stackRowPush++;						\
	    *stackColPush=n;						\
	    stackColPush++;						\
	    rowLabel_current[n]=label;					\
	  }								\
	}								\
									\
	while(stackRowPush!=stackRow) {					\
	  stackRowPush--;						\
	  stackColPush--;						\
	  m=(*stackRowPush);						\
	  n=(*stackColPush);						\
									\
	  nhb = (((m%2)==1) ? nhbOdd : nhbEven);			\
									\
	  flgr2d_get_neighborhood_##dtype(extr,imgsrc,nhb,m,n);		\
									\
	  for(t=0 ; t<extr->size[0] ; t++) {				\
	    u=list_y[t];						\
	    v=list_x[t];						\
	    k=m+u-1;							\
	    l=n+v-1;							\
	    rowLabel_current = (dtype *) imglabel->array[k];		\
	    if((pextr[t]==currentPixel)&&(rowLabel_current[l]==0)) {	\
	      *stackRowPush=k;						\
	      stackRowPush++;						\
	      *stackColPush=l;						\
	      stackColPush++;						\
	      rowLabel_current[l]=label;				\
	    }								\
	  }								\
									\
	}								\
      }									\
    }									\
  }									\
  									\
  flgr2d_destroy(nhbOdd);						\
  flgr2d_destroy(nhbEven);						\
  flgr2d_destroy_neighbor_box(extr);					\
  free(stackRow);							\
  free(stackCol);							\
  return label;



int flgr2d_label_fgBIT(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_BIT);
  return FLGR_RET_NOT_IMPLEMENTED;
}

int flgr2d_label_fgUINT8(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  lbl(fgUINT8,FLGR_UINT8);
}


int flgr2d_label_fgUINT16(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  lbl(fgUINT16,FLGR_UINT16);
}


int flgr2d_label_fgUINT32(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  lbl(fgUINT32,FLGR_UINT32);
}

int flgr2d_label_fgINT8(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_INT8);
  return FLGR_RET_NOT_IMPLEMENTED;
}

int flgr2d_label_fgINT16(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_INT16);
  return FLGR_RET_NOT_IMPLEMENTED;
}

int flgr2d_label_fgINT32(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_INT32);
  return FLGR_RET_NOT_IMPLEMENTED;
}

int flgr2d_label_fgFLOAT32(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_FLOAT32);
  return FLGR_RET_NOT_IMPLEMENTED;
}

int flgr2d_label_fgFLOAT64(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  flgr_no_define_type_function(FLGR_FLOAT64);
  return FLGR_RET_NOT_IMPLEMENTED;
}



////////////////////////////////////////////////////////////////////////////
/*! Labelize connected components
 *  @param imglabel : labelized image
 *  @param imgsrc : image source
 *  @param connexity : connexity (4-connexity,6-connexity,8-connexity)
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
int flgr2d_label(FLGR_Data2D *imglabel, FLGR_Data2D *imgsrc, FLGR_Connexity connexity) {
  int ret;

  FLGR_DECLARE_FUNCTION;

  if((imglabel==NULL) || (imgsrc==NULL)){
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(imglabel->spp!=1)  {
    POST_ERROR("Multi Sample Per Pixel not supported\n");
    return FLGR_RET_UNDEFINED_ERROR;
  }

  if((ret=flgr2d_is_data_same_attributes(imglabel,imgsrc,__FUNCTION__))!=FLGR_RET_OK) return ret;

  FLGR_DISPATCH_FUNCTION(FLGR_RET_TYPE_UNKNOWN,imglabel->type,flgr2d_label,imglabel,imgsrc,connexity);

}




//! @}
